# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import hmac
import hashlib
import logging
import time
import urllib.parse

import requests

import coin.exchange.base.kr_rest.rest_client_base as rcb
from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin.exchange.util.rate_limit import RateLimiter
from coin.exchange.liqui_v3.kr_rest.constants import symbol_list


class LiquiRestQueryError(Exception):
  pass


class LiquiOrderBase(object):
  def __init__(self, key_file, timeout=5):
    key = AuthKey.from_file(key_file)
    self.access_key = key.access_key
    self.secret_key = key.secret_key
    self.rate_limit = RateLimiter(1, 2.0)
    self.api_url = "https://api.liqui.io/tapi"
    self.timeout = timeout
    self._logger = logging.getLogger(__name__)

  def _get_signature(self, params):
    assert isinstance(params, dict)
    params['nonce'] = int(time.time())
    encoded_params = urllib.parse.urlencode(params)
    signature = hmac.new(self.secret_key.encode('utf8'),
                         encoded_params.encode("utf8"),
                         hashlib.sha512)
    return signature.hexdigest()

  def _query_nocheck(self, full_url, request_type, params):
    headers = {'Key': self.access_key, 'Sign': self._get_signature(params)}
    req_timestamp = time.time()
    if request_type == 'GET':
      response = requests.get(full_url, params=params, headers=headers, timeout=self.timeout)
    elif request_type == 'POST':
      response = requests.post(full_url, data=params, headers=headers, timeout=self.timeout)
    else:
      raise ValueError('Invalid request type')
    res_timestamp = time.time()

    if response.status_code == 200:
      response_json = response.json()
      if not response_json['success']:
        self._logger.error('Liqui private rest query failed!\n %s', response_json)
        raise LiquiRestQueryError()
      return rcb.RestUpdate(req_timestamp, res_timestamp, response_json)
    else:
      self._logger.error("HTTP code: %d\nbody:%s\n" % (response.status_code, response.text))
      raise LiquiRestQueryError()

  def _query(self, request_type, params=None, disable_rate_limit=False):
    if params:
      params = {key: value for key, value in params.items() if value is not None}
    else:
      params = dict()

    if not disable_rate_limit:
      while not self.rate_limit.check():
        time.sleep(0.1)
    else:
      self.rate_limit.check()
    return self._query_nocheck(self.api_url, request_type, params=params)


class LiquiNativePrivateClient(LiquiOrderBase):
  def __init__(self, key_file):
    LiquiOrderBase.__init__(self, key_file)

  def query_getinfo(self):
    method = "getInfo"
    params = {'method': method}
    update = self._query('POST', params)
    return update

  def query_trade(self, pair, trade_type, rate, amount):
    assert pair in symbol_list
    assert trade_type in ('buy', 'sell')
    method = 'Trade'
    params = {
        'method': method,
        'pair': pair,
        'type': trade_type,
        'rate': rate,
        'amount': amount,
    }
    update = self._query('POST', params)
    return update

  def query_active_orders(self, pair):
    method = 'ActiveOrders'
    params = {
        'method': method,
        'pair': pair,
    }
    update = self._query('POST', params)
    return update

  def query_order_info(self, order_id):
    method = 'OrderInfo'
    params = {
        'method': method,
        'order_id': order_id,
    }
    update = self._query('POST', params)
    return update

  def query_cancel_order(self, order_id):
    method = 'CancelOrder'
    order_id = order_id or -1  # -1 returns all unfilled orders
    params = {
        'method': method,
        'order_id': order_id,
    }
    update = self._query("POST", params)
    return update

  def query_trade_history(self,
                          pair,
                          *,
                          count=1000,
                          from_id=None,
                          end_id=None,
                          since=None,
                          end=None,
                          sorting_order=None):
    assert sorting_order in ('ASC', 'DESC', None)
    method = 'TradeHistory'
    params = {
        'method': method,
        'pair': pair,
        'count': count,
        'from_id': from_id,
        'end_id': end_id,
        'since': since,
        'end': end,
        'order': sorting_order,
    }
    update = self._query('POST', params)
    return update
